package com.hydra.cron.server.cron;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.hydra.cron.common.constants.C;
import com.hydra.cron.common.curator.helper.CuratorHelper;
import com.hydra.cron.common.curator.selector.RunningListener;
import com.hydra.cron.common.utils.LogUtil;
import com.hydra.cron.common.utils.ObjectUtil;
import com.hydra.cron.server.model.CronServer;
import com.hydra.cron.server.model.CronTask;
import com.hydra.cron.server.quartz.QuartzManager;
import com.hydra.cron.server.quartz.SimpleJob;
import org.apache.curator.framework.recipes.cache.NodeCacheListener;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * hdyraCron Created by ZhengGong on 15/12/13.
 * PackageName com.hydra.cron.server.cron
 * Description
 */

@Service
public class CronManager {
    private static final Logger LOG = LoggerFactory.getLogger(CronManager.class);
    private static final String TOKEN = UUID.randomUUID().toString();

    private Map<String, CronTask> cronTaskMap = Maps.newConcurrentMap();

    private final AtomicInteger STATUS = new AtomicInteger(CronStatus.CLOSE);

    public void start() throws Exception {
        if (STATUS.compareAndSet(CronStatus.CLOSE, CronStatus.OPEN)) {
            reload();
        }

        CuratorHelper.getInstance().watch(C.PATH.CRON_PATH, new NodeCacheListener() {
            public void nodeChanged() throws Exception {
                reload();
            }
        });
    }

    public void close() throws SchedulerException, IOException {
        QuartzManager.shutdownJobs();
        STATUS.set(CronStatus.CLOSE);
        CuratorHelper.getInstance().stopTakeLeader();
    }

    @PostConstruct
    public void init() throws Exception {
        // 抢占
        CuratorHelper.getInstance().takeLeader(C.PATH.SERVER_MASTER, new RunningListener() {
            public void processStart() {

            }

            public void processStop() {

            }

            public void processActiveEnter() {
                try {
                    start();
                } catch (Exception e) {
                    LogUtil.logError(LOG, e.getMessage(), e);
                    try {
                        CuratorHelper.getInstance().stopTakeLeader();
                    } catch (IOException e1) {
                        LOG.error("close", e1);
                    }
                }
            }

            public void processActiveExit() {
                try {
                    close();
                    CuratorHelper.getInstance().closeNodeCache();
                } catch (SchedulerException e) {
                    LogUtil.logError(LOG, e.getMessage(), e);
                } catch (IOException e) {
                    LogUtil.logError(LOG, e.getMessage(), e);
                } catch (Exception e) {
                    LogUtil.logError(LOG, e.getMessage(), e);
                }
            }
        });

        // 添加节点标记
        CuratorHelper.getInstance().createEphemeral(C.PATH.SERVER_PATH + "/" + TOKEN, JSONObject.toJSONString(new CronServer()));
    }

    public synchronized void reload() throws Exception {
        List<CronTask> list = JSONArray.parseArray(CuratorHelper.getInstance().getData(C.PATH.CRON_PATH), CronTask.class);

        Set<String> keySet = Sets.newHashSet(cronTaskMap.keySet());

        if (ObjectUtil.isNotEmpty(list)) {
            for (CronTask cronTask : list) {
                keySet.remove(cronTask.getJobKey());
                CronTask currentTask = cronTaskMap.get(cronTask.getJobKey());
                if (ObjectUtil.isNotNull(currentTask)) {
                    if (cronTask.getVersion() > currentTask.getVersion()) {
                        QuartzManager.modifyJobTime(cronTask.getJobKey(), cronTask.getCronExpression(), cronTask);
                        cronTaskMap.put(cronTask.getJobKey(), cronTask);
                    }
                } else {
                    QuartzManager.addJob(cronTask.getJobKey(), SimpleJob.class, cronTask.getCronExpression(), cronTask);
                    cronTaskMap.put(cronTask.getJobKey(), cronTask);
                }
            }
        }

        for (String key : keySet) {
            QuartzManager.removeJob(key);
        }
    }

    public static class CronStatus {
        public static final int CLOSE = 0;
        public static final int OPEN = 1;
    }
}
